package entities;

import entities.forum.Message;
import entities.extra.UserTripParticipation;
import entities.location.Address;
import entities.userstate.AwaitingApprovalUser;
import entities.userstate.BannedUser;
import entities.userstate.CancelledUser;
import entities.profiles.DriverProfile;
import entities.profiles.PassengerProfile;
import entities.forum.Post;
import entities.forum.Topic;
import entities.reports.ReportedUser;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Objects;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * Trabalho PD - 2014/2015
 * @author a21210380 a21210392
 */
@Entity
@Table(name = "user")
@NamedQueries({
    @NamedQuery(name = "User.findAll", query = "SELECT u FROM User u"),
    @NamedQuery(name = "User.findByIdUser", query = "SELECT u FROM User u WHERE u.idUser = :idUser"),
    @NamedQuery(name = "User.findByName", query = "SELECT u FROM User u WHERE u.name = :name"),
    @NamedQuery(name = "User.findByBi", query = "SELECT u FROM User u WHERE u.bi = :bi"),
    @NamedQuery(name = "User.findByEmail", query = "SELECT u FROM User u WHERE u.email = :email"),
    @NamedQuery(name = "User.getLastOnlineUsers", query = "SELECT u FROM User u WHERE u.lastActionTime >= :minusServerDate AND u.lastActionTime <= :serverDate")})
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "idUser")
    private Integer idUser;
    
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 100)
    @Column(name = "name")
    private String name;
    
    @Basic(optional = false)
    @NotNull
    @Column(name = "bi")
    private int bi;
    
    // @Pattern(regexp="[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", message="Invalid email")//if the field contains email address consider using this annotation to enforce field validation
    @Basic(optional = false)
    @NotNull
    @Size(min = 1, max = 60)
    @Column(name = "email")
    private String email;
    
    @Basic(optional = false)
    @NotNull
    @Column(name = "lastActionTime")
    @Temporal(TemporalType.TIMESTAMP)
    private Date lastActionTime;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idUser")
    private Collection<Vehicle> vehicles;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idUser")
    private Collection<Topic> topics;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idUser")
    private Collection<Post> posts;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idUser")
    private Collection<ReportedUser> reports;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
    private Collection<UserTripParticipation> trips;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idReceivedUser")
    private Collection<Message> receivedMessages;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "idSentUser")
    private Collection<Message> messagesSent;
    
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
    private CancelledUser cancelledUser;
    
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
    private BannedUser bannedUser;
    
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
    private AwaitingApprovalUser awaitingApprovalUser;
    
    @JoinColumn(name = "idPassengerProfile", referencedColumnName = "idPassengerProfile")
    @OneToOne(optional = false)
    private PassengerProfile idPassengerProfile;
    
    @JoinColumn(name = "idlogin", referencedColumnName = "idlogin")
    @OneToOne(optional = false)
    private Login idlogin;
    
    @JoinColumn(name = "idDriverProfile", referencedColumnName = "idDriverProfile")
    @OneToOne(optional = false)
    private DriverProfile idDriverProfile;
    
    @JoinColumn(name = "idAddress", referencedColumnName = "idAddress")
    @ManyToOne(optional = false)
    private Address idAddress;
    
    @ManyToMany(mappedBy = "missingEvaluations")
    private Collection<Trip> evaluations;

    public User() {
    }

    public User(Login login, PassengerProfile passengerProfile, DriverProfile driverProfile, Address idAddress, String name, int bi, String email, Date lastActionTime) {
        this.idlogin = login;
        
        this.idAddress = idAddress;
        this.name = name;
        this.bi = bi;
        this.email = email;
        this.lastActionTime = lastActionTime;
        
        this.idPassengerProfile = passengerProfile;
        this.idDriverProfile = driverProfile;
        
        this.vehicles = new ArrayList<>();
        this.topics = new ArrayList<>();
        this.posts = new ArrayList<>();
        this.reports = new ArrayList<>();
        this.trips = new ArrayList<>();
        this.receivedMessages = new ArrayList<>();
        this.messagesSent = new ArrayList<>();
        this.evaluations = new ArrayList<>();
    }

    public Integer getId() {
        return idUser;
    }
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getBi() {
        return bi;
    }

    public void setBi(int bi) {
        this.bi = bi;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Date getLastActionTime() {
        return lastActionTime;
    }

    public void setLastActionTime(Date lastActionTime) {
        this.lastActionTime = lastActionTime;
    }

    public Collection<Topic> getTopics() {
        return topics;
    }
    
    public void addToEvaluation(Trip t) {
        evaluations.add(t);
    }

    public void addTopic(Topic topic) {
        topics.add(topic);
    }

    public CancelledUser getCancelledUser() {
        return cancelledUser;
    }

    public void setCancelledUser(CancelledUser cancelledUser) {
        this.cancelledUser = cancelledUser;
    }
    
    public void activateCancelledAccount() {
        this.cancelledUser = null;
    }

    public Collection<Post> getPosts() {
        return posts;
    }

    public void addPost(Post post) {
        this.posts.add(post);
    }

    public Collection<Vehicle> getVehicles() {
        return vehicles;
    }
    
    public boolean havesVehicle(Vehicle v) {
        for(Vehicle vehicle : vehicles) {
            if(Objects.equals(vehicle.getId(), v.getId())) {
                return true;
            }
        }
        return false;
    }

    public void addVehicle(Vehicle vehicle) {
        if(this.vehicles.add(vehicle)) {
            vehicle.setUser(this);
        }
    }
    
    public void removeVehicle(Vehicle vehicle) {
        if(this.vehicles.remove(vehicle)) {
            vehicle.setUser(null);
        }
    }
    
    public Collection<UserTripParticipation> getUserTripParticipations() {
        return trips;
    }

    public void participateInTrip(UserTripParticipation trip) {
        this.trips.add(trip);
    }

    public Collection<Message> getReceivedMessages() {
        return receivedMessages;
    }
    
    public boolean deleteReceivedMessage(Message m) {
        if(receivedMessages.contains(m)) {
            receivedMessages.remove(m);
            return true;
        }
        return false;
    }

    public void addReceivedMessage(Message message) {
        this.receivedMessages.add(message);
    }

    public Collection<Message> getSentMessage() {
        return messagesSent;
    }

    public void addSentMessage(Message message) {
        this.messagesSent.add(message);
    }

    public BannedUser getBannedUser() {
        return bannedUser;
    }

    public void banUser(BannedUser bannedUser) {
        this.bannedUser = bannedUser;
    }

    public AwaitingApprovalUser getAwaitingApprovalUser() {
        return awaitingApprovalUser;
    }
    
    public void unapproveUser(AwaitingApprovalUser awaitingApprovalUser) {
        this.awaitingApprovalUser = awaitingApprovalUser;
    }
    
    public void approveUser() {
        this.awaitingApprovalUser = null;
    }

    public PassengerProfile getPassengerProfile() {
        return idPassengerProfile;
    }

    public Login getLogin() {
        return idlogin;
    }
    
    public DriverProfile getDriverProfile() {
        return idDriverProfile;
    }
    
    public Address getAddress() {
        return idAddress;
    }

    public void setAddress(Address idAddress) {
        this.idAddress = idAddress;
    }
    
     public Collection<ReportedUser> getReportsOnThisUser() {
        return reports;
    }

    public void reportUser(ReportedUser report) {
        this.reports.add(report);
    }
    
    public void evaluatedTrip(Trip trip) {
        evaluations.remove(trip);
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (idUser != null ? idUser.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        if(object == null) {
            return false;
        }
        if (!(object instanceof User)) {
            return false;
        }
        User other = (User) object;
        return !((this.idUser == null && other.idUser != null) || (this.idUser != null && !this.idUser.equals(other.idUser)));
    }

    @Override
    public String toString() {
        return "entities.User[ idUser=" + idUser + " ]";
    }

    public Collection<Trip> getEvaluations() {
        return evaluations;
    }
}
